const moment = require('moment');
const { Controller } = require('../lib/contorller');
const { kvModel, historyModel } = require('../lib/mongo');

module.exports = class ConsulController extends Controller {
  /**
   * 获取子key列表
   */
  async getChildKey() {
    const { ctx } = this;
    const { parentKey, dc } = ctx.query;
    ctx.result.list = await kvModel.find({ dc, key: new RegExp(`^${ parentKey }`) }).lean();
    ctx.result.success = true;
  }

  /**
   * 获取key对应的值
   */
  async getValue() {
    const { ctx } = this;
    const { key, dc } = ctx.query;
    ctx.result.info = await kvModel.findOne({ dc, key }).lean();
    ctx.result.success = true;
  }

  /**
   * 创建key
   */
  async createKey() {
    const { ctx } = this;
    const { key, value, dc, checkerId, describe, type } = ctx.request.body;
    try {
      let kvInfo = await kvModel.findOne({ key, dc });
      if (kvInfo) {
        ctx.result.message = 'key值已存在！';
        return;
      }
      const updateTime = moment().format('yyyy-MM-DD hh:mm:ss');
      const history = await historyModel.create({
        updateUser: ctx.user.name,
        updateTime,
        action: 'create',
        newValue: value,
        oldValue: '',
        success: false,
      });
      ctx.result.historyId = history._id;
      const [createErr, createRes] = await ctx.consul.updateKey(key, value, dc);
      if (createErr || !createRes) {
        history.errorReson = ctx.result.message = `${ createErr?.message || '' } 创建key失败`;
        this.log.error(ctx.result.message);
        await history.save();
        return;
      }
      const [readErr, [consulKv]] = await ctx.consul.readKey(key, { dc });
      if (readErr || !consulKv) {
        history.errorReson = ctx.result.message = `${ readErr?.message || '' } 查询创建后的key失败`;
        await history.save();
        this.log.error(ctx.result.message);
        return;
      }
      kvInfo = await kvModel.create({
        checkerId,
        key,
        dc,
        parentKey: key.split('/').filter(i => i).slice(0, -1).join('/'),
        value,
        describe,
        updateUser: ctx.user.name,
        updateTime,
        type,
        createIndex: consulKv.CreateIndex,
        modifyIndex: consulKv.ModifyIndex,
        lockIndex: consulKv.LockIndex,
        flags: consulKv.Flags,
      });
      history.kvId = kvInfo._id;
      history.success = true;
      history.save();
      ctx.result.info = kvInfo.toObject();
      ctx.result.success = true;
    } catch (error) {
      this.log.error(error);
      ctx.result.message = '创建失败';
    }
  }

  /**
   * 更新key
   */
  async updateKey() {
    const { ctx } = this;
    const { _id, action, value, dc, modifyIndex } = ctx.request.body;
    const info = await kvModel.findById(_id);
    if (!info) {
      ctx.result.message = '未找到对应的key';
      return;
    }

    info.updateUser = ctx.user.name;
    info.updateTime = moment().format('yyyy-MM-DD hh:mm:ss');

    if (info.modifyIndex !== modifyIndex) {
      ctx.result.message = '值在本次更新前已发生变更，请刷新页面查看！';
      return;
    }

    if (info.value === value) {
      ctx.result.message = '值未发生变化，无需保存！';
      return;
    }

    // 插入操作历史
    const history = await historyModel.create({
      kvId: _id,
      updateUser: ctx.user.name,
      updateTime: info.updateTime,
      action,
      newValue: value,
      oldValue: info.value,
      success: false,
      modifyIndex: info.modifyIndex,
    });
    ctx.result.historyId = history._id;

    // 创建session
    const [createSessionErr, sessionId] = await ctx.consul.createSession({
      dc,
      TTL: '10s',
      Behavior: 'release',
      Name: Date.now().toString(),
    });
    if (createSessionErr || !sessionId) {
      this.log.error('创建session失败', dc);
      history.errorReson = ctx.result.message = `${ createSessionErr?.message || '' } 创建session失败`;
      history.save();
      return;
    }

    try {
      // 更新key
      const [updateErr, success] = await ctx.consul.updateKey(info.key, value, dc, sessionId);
      if (updateErr || !success) {
        history.errorReson = ctx.result.message = `${ updateErr?.message || '' } key仍在保护期，请稍后重试！`;
        throw updateErr;
      }

      // 更新modifyIndex
      const [readErr, consulKvInfo] = await ctx.consul.readKey(info.key, { dc });
      if (readErr || !consulKvInfo) {
        history.errorReson = ctx.result.message = `${ readErr?.message || '' } 更新key成功后，查询modifyIndex失败`;
      } else {
        info.modifyIndex = consulKvInfo[0].ModifyIndex;
      }
      info.value = value;

      // 标记历史操作成功
      history.success = true;
      await Promise.all([
        info.save(),
        history.save(),
      ]);
      ctx.result.info = info.toObject();
      ctx.result.success = true;
    } catch (error) {
      this.log.error(error, info.key, value, dc, sessionId);
    }

    // 删除session
    const delSuccess = await ctx.consul.deleteSession(sessionId, dc);
    if (!delSuccess) {
      this.log.error('删除session失败');
    }
  }

  /**
   * 删除key
   */
  async deleteKey() {
    const { ctx } = this;
    const { dc, key } = ctx.query;
    const isDir = ctx.query.isDir === 'true';
    const modifyIndex = Number(ctx.query.modifyIndex);
    try {
      const info = await kvModel.findOne({ key }).lean();
      // consul的限制，在特定情况下创建深层key，会没有父key的存储记录
      if (!info && !isDir) {
        ctx.result.message = '不存在的key';
        return;
      }
      const history = await historyModel.create({
        kvId: info?._id,
        updateUser: ctx.user.name,
        updateTime: moment().format('yyyy-MM-DD hh:mm:ss'),
        action: 'delete',
        newValue: '',
        oldValue: info?.value,
        success: false,
        modifyIndex: modifyIndex,
      });
      ctx.result.historyId = history._id;
      const [delErr, delRes] = await ctx.consul.deleteKey(key, { dc, modifyIndex, recurse: true });
      if (delErr || !delRes) {
        history.errorReson = ctx.result.message = `${ delErr?.message || '' } 删除失败`;
        history.save();
        return;
      }
      history.success = true;
      await Promise.all([
        kvModel.deleteMany({ key: new RegExp(`^${ key }`) }),
        history.save(),
      ]);
      ctx.result.success = true;
    } catch (error) {
      this.log.error(error);
      ctx.result.message = error?.message;
    }
  }

  /**
   * 获取历史
   */
  async getHistory() {
    const { ctx } = this;
    const { kvId } = ctx.query;
    try {
      ctx.result.list = await historyModel.find({ kvId }).sort({ updateTime: -1 }).lean();
      ctx.result.success = true;
    } catch (error) {
      this.log.error(error);
      ctx.result.message = error?.message;
    }
  }
};
